package com.hn.birthdaymatch.utils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Base64;

/**
 * SaltedEncryptionUtils是一个加密工具类，用于对用户密码进行加密和解密操作。
 *
 * 加密解密步骤：
 * 1. 使用固定的盐值对密码进行加密：
 *    a. 将盐值和密码转换为字节数组。
 *    b. 使用盐值生成密钥。
 *    c. 创建AES加密算法实例。
 *    d. 使用密钥初始化加密算法实例。
 *    e. 执行加密操作，将密码字节数组转换为加密后的字节数组。
 *    f. 将加密后的字节数组进行Base64编码，得到加密后的密码字符串。
 *    g. 返回加密后的密码字符串。
 *
 * 2. 使用固定的盐值对加密后的密码进行解密：
 *    a. 将盐值和加密后的密码字符串转换为字节数组。
 *    b. 使用盐值生成密钥。
 *    c. 创建AES解密算法实例。
 *    d. 使用密钥初始化解密算法实例。
 *    e. 执行解密操作，将加密后的密码字节数组转换为解密后的字节数组。
 *    f. 将解密后的字节数组转换为字符串。
 *    g. 返回解密后的密码字符串。
 *
 * 注意事项：
 * - 每次加密相同的密码会生成不同的加密后的密文。
 * - 解密操作需要使用与加密操作相同的盐值和密钥。
 *
 * 使用示例：
 * String password = "123456";
 *
 * // 加密密码
 * String encryptedPassword = SaltedEncryptionUtils.encryptPassword(password);
 * System.out.println("加密后的密码：" + encryptedPassword);
 *
 * // 解密密码
 * String decryptedPassword = SaltedEncryptionUtils.decryptPassword(encryptedPassword);
 * System.out.println("解密后的密码：" + decryptedPassword);
 *
 * // 比较密码
 * boolean match = SaltedEncryptionUtils.comparePasswords(password, encryptedPassword);
 * System.out.println("密码与加密后的密码是否匹配：" + match);
 */
public class SaltedEncryptionUtils {

    /**
     * 盐值，用于加密过程中增加密码的复杂性和安全性。在这个示例中，盐值被设置为固定的字符串"lto-need"，你可以根据需要自定义不同的盐值*
     */
    static final String SALT_VALUE = "tcb-birthday";

    /**
     * 表示所使用的加密算法是AES（高级加密标准）。AES是一种对称加密算法，它可以用于对数据进行加密和解密。*
     */
    private static final String AES = "AES";

    /**
     * 初始化向量的长度。在加密过程中，每次生成随机的初始化向量（IV）是很重要的。
     * 在这个示例中，初始化向量的长度被设置为16字节（128位），你可以根据需要调整该值。*
     */
    private static final int IV_LENGTH = 16;

    /**
     * 对密码进行加密。
     *
     * @param password 要加密的密码
     * @return 加密后的密码字符串（以Base64编码形式表示）
     */
    public static String encrypt(String password) {
        try {
            byte[] saltBytes = SALT_VALUE.getBytes();
            byte[] passwordBytes = password.getBytes();

            // 使用盐值生成密钥
            SecretKeySpec secretKeySpec = generateKey(saltBytes);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

            // 生成随机的初始化向量
            byte[] iv = generateIV();

            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);

            byte[] encryptedBytes = cipher.doFinal(passwordBytes);
            byte[] resultBytes = new byte[IV_LENGTH + encryptedBytes.length];
            System.arraycopy(iv, 0, resultBytes, 0, IV_LENGTH);
            System.arraycopy(encryptedBytes, 0, resultBytes, IV_LENGTH, encryptedBytes.length);

            return Base64.getEncoder().encodeToString(resultBytes); // 返回加密后的密码字符串
        } catch (Exception e) {
            throw new RuntimeException("密文的长度与预期长度不符,数组访问超出范围");
        }
    }

    /**
     * 对密码进行解密。
     *
     * @param encryptedPassword 加密后的密码字符串（以Base64编码形式表示）
     * @return 解密后的密码字符串
     */
    public static String decrypt(String encryptedPassword) {
        try {
            byte[] saltBytes =  SALT_VALUE.getBytes();

            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedPassword);

            byte[] iv = new byte[IV_LENGTH];
            System.arraycopy(encryptedBytes, 0, iv, 0, IV_LENGTH);

            // 使用盐值生成密钥
            SecretKeySpec secretKeySpec = generateKey(saltBytes);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);

            byte[] decryptedBytes = cipher.doFinal(encryptedBytes, IV_LENGTH, encryptedBytes.length - IV_LENGTH);
            return new String(decryptedBytes);
        } catch (Exception e) {
            throw new RuntimeException("密文的长度与预期长度不符,数组访问超出范围");
        }
    }

    /**
     * 比较密码明文与加密后的密码是否匹配。
     *
     * @param plainPassword     明文密码
     * @param encryptedPassword 加密后的密码字符串（以Base64编码形式表示）
     * @return 是否匹配
     */
    public static boolean compare(String plainPassword, String encryptedPassword) {
        String decryptedPassword = decrypt(encryptedPassword);
        return plainPassword.equals(decryptedPassword);
    }

    // 使用盐值生成密钥
    private static SecretKeySpec generateKey(byte[] salt) throws Exception {
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
        byte[] hashedBytes = messageDigest.digest(salt);

        return new SecretKeySpec(hashedBytes, 0, 16, AES);
    }

    // 生成随机的初始化向量
    private static byte[] generateIV() {
        byte[] iv = new byte[IV_LENGTH];
        for (int i = 0; i < IV_LENGTH; i++) {
            iv[i] = (byte) (Math.random() * 256);
        }
        return iv;
    }
}
